package coreModel

import (
	"fmt"
	"reflect"
	"regexp"
	"strings"

	"gorm.io/gorm"
)

type NullType byte

const (
	_ NullType = iota
	// IsNull the same as `is null`
	IsNull
	// IsNotNull the same as `is not null`
	IsNotNull
)

func IsExpString(s string) bool {
	// 匹配 "exp" 或 "exp" + 正整数
	pattern := `^exp(\d+)?$`
	re, err := regexp.Compile(pattern)
	if err != nil {
		return false
	}
	return re.MatchString(s)
}

// WhereBuild sql build where
func WhereBuild(ado *gorm.DB, where map[string]any) (whereSQL string, vals []any, err error) {
	for k, v := range where {
		ks := strings.Split(k, " ")
		if len(ks) > 2 {
			return "", nil, fmt.Errorf("Error in query condition: %s. ", k)
		}

		if whereSQL != "" {
			whereSQL += " AND "
		}
		//strings.Join(ks, ",")
		switch len(ks) {
		case 1:
			if IsExpString(k) { // 匹配 `"exp" 或 "exp" + 正整数` 直接拼接sql
				whereSQL += v.(string)
			} else {
				switch v := v.(type) {
				case NullType:
					if v == IsNotNull {
						whereSQL += fmt.Sprint(k, " IS NOT NULL")
					} else {
						whereSQL += fmt.Sprint(k, " IS NULL")
					}
				default:
					whereSQL += fmt.Sprint(k, "=?")
					vals = append(vals, v)
				}
			}
		case 2:
			k = ks[0]
			switch strings.ToLower(ks[1]) {
			case "=":
				whereSQL += fmt.Sprint(k, "=?")
				vals = append(vals, v)
			case ">":
				whereSQL += fmt.Sprint(k, ">?")
				vals = append(vals, v)
			case ">=":
				whereSQL += fmt.Sprint(k, ">=?")
				vals = append(vals, v)
			case "<":
				whereSQL += fmt.Sprint(k, "<?")
				vals = append(vals, v)
			case "<=":
				whereSQL += fmt.Sprint(k, "<=?")
				vals = append(vals, v)
			case "!=":
				whereSQL += fmt.Sprint(k, "!=?")
				vals = append(vals, v)
			case "<>":
				whereSQL += fmt.Sprint(k, "!=?")
				vals = append(vals, v)
			case "between":
				switch rv := reflect.ValueOf(v); rv.Kind() {
				case reflect.Slice, reflect.Array:
					if rv.Len() == 2 {
						whereSQL += fmt.Sprint(k, " between ? AND ?")
						for i := 0; i < rv.Len(); i++ {
							vals = append(vals, rv.Index(i).Interface())
						}
					} else {
						return "", nil, fmt.Errorf("between vals error: %s. ", v)
					}
				default:
					return "", nil, fmt.Errorf("between vals error: %s. ", v)
				}
			case "in":
				switch rv := reflect.ValueOf(v); rv.Kind() {
				case reflect.Slice, reflect.Array:
					if rv.Len() == 0 {
						whereSQL += fmt.Sprint(k, " in (NULL)")
					} else {
						replaceVar := strings.Repeat("?,", rv.Len())
						replaceVar = strings.Trim(replaceVar, ",")
						inSql := fmt.Sprint(k, " in ("+replaceVar+")")
						var inVals []any
						for i := 0; i < rv.Len(); i++ {
							inVals = append(inVals, rv.Index(i).Interface())
						}
						whereSQL += ado.Dialector.Explain(inSql, inVals...)
					}
				default:
					whereSQL += fmt.Sprint(k, " in ?")
					vals = append(vals, v)
				}
			case "notin":
				switch rv := reflect.ValueOf(v); rv.Kind() {
				case reflect.Slice, reflect.Array:
					if rv.Len() == 0 {
						whereSQL = strings.Trim(whereSQL, " AND ")
					} else {
						replaceVar := strings.Repeat("?,", rv.Len())
						replaceVar = strings.Trim(replaceVar, ",")
						inSql := fmt.Sprint(k, " not in ("+replaceVar+")")
						var inVals []any
						for i := 0; i < rv.Len(); i++ {
							inVals = append(inVals, rv.Index(i).Interface())
						}
						whereSQL += ado.Dialector.Explain(inSql, inVals...)
					}
				default:
					whereSQL += fmt.Sprint(k, " not in ?")
					vals = append(vals, v)
				}
			case "like":
				whereSQL += fmt.Sprint(k, " like ?")
				vals = append(vals, v)
			case "notlike":
				whereSQL += fmt.Sprint(k, " not like ?")
				vals = append(vals, v)
			case "regexp":
				whereSQL += fmt.Sprint(k, " regexp ?")
				vals = append(vals, v)
			case "notregexp":
				whereSQL += fmt.Sprint(k, " not regexp ?")
				vals = append(vals, v)
			}
		}
	}
	return
}

// joinString 将 string 切片转换为逗号分隔的字符串
func joinString(val []string, sep string) string {
	strValList := make([]string, len(val))
	for i, v := range val {
		strValList[i] = fmt.Sprintf("%s", escapeString(v))
	}
	return strings.Join(strValList, sep)
}

// escapeString 对字符串进行转义，替换特殊字符为转义后的形式
func escapeString(s string) string {
	// 替换单引号为两个单引号
	s = strings.ReplaceAll(s, "'", "''")
	// 替换双引号为两个双引号
	s = strings.ReplaceAll(s, `"`, `""`)
	// 替换反斜杠为两个反斜杠
	s = strings.ReplaceAll(s, `\`, `\\`)
	// 替换百分号为转义后的百分号
	s = strings.ReplaceAll(s, `%`, `\%`)
	// 替换下划线为转义后的下划线
	s = strings.ReplaceAll(s, `_`, `\_`)

	return s
}
